// admin/system/dao/sys_menu_dao.go
package dao

import (
	"go-admin1/admin/system/model"
	"go-admin1/internal/infrastructure/database"
	"go-admin1/internal/infrastructure/logger"

	"go.uber.org/zap"
	"gorm.io/gorm"
)

// SysMenuDao 菜单数据访问接口
type SysMenuDao interface {
	Create(menu *model.SysMenu) error
	GetByID(id int64) (*model.SysMenu, error)
	Update(menu *model.SysMenu) error
	Delete(id int64) error
	List(page, pageSize int) ([]*model.SysMenu, int64, error)
	ListAll() ([]*model.SysMenu, error)
	ListByParentID(parentID int64) ([]*model.SysMenu, error)
	ListByRoleID(roleID int64) ([]*model.SysMenu, error)
	GetMenuPermsByRoleId(roleID int64) map[string]bool
	GetMenuTreeAll() []*model.SysMenu
	GetMenuTreeByUserId(userId int64) []*model.SysMenu
}

type sysMenuDao struct {
	db *gorm.DB
}

// NewSysMenuDao 创建菜单数据访问实例
func NewSysMenuDao() SysMenuDao {
	return &sysMenuDao{
		db: database.GetDB(),
	}
}

func (m *sysMenuDao) Create(menu *model.SysMenu) error {
	logger.Info("创建菜单", zap.Int64("menu_id", menu.MenuID))
	return m.db.Create(menu).Error
}

func (m *sysMenuDao) GetByID(id int64) (*model.SysMenu, error) {
	logger.Debug("根据ID获取菜单", zap.Int64("menu_id", id))
	var menu model.SysMenu
	err := m.db.Where("menu_id = ? AND status = '0'", id).First(&menu).Error
	return &menu, err
}

func (m *sysMenuDao) Update(menu *model.SysMenu) error {
	logger.Info("更新菜单", zap.Int64("menu_id", menu.MenuID))
	return m.db.Save(menu).Error
}

func (m *sysMenuDao) Delete(id int64) error {
	logger.Info("删除菜单", zap.Int64("menu_id", id))
	return m.db.Where("menu_id = ?", id).Delete(&model.SysMenu{}).Error
}

func (m *sysMenuDao) List(page, pageSize int) ([]*model.SysMenu, int64, error) {
	logger.Debug("获取菜单列表", zap.Int("page", page), zap.Int("page_size", pageSize))
	var menus []*model.SysMenu
	var total int64

	offset := (page - 1) * pageSize
	db := m.db.Model(&model.SysMenu{}).Where("status = '0'")

	if err := db.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	if err := db.Offset(offset).Limit(pageSize).Find(&menus).Error; err != nil {
		return nil, 0, err
	}

	return menus, total, nil
}

func (m *sysMenuDao) ListAll() ([]*model.SysMenu, error) {
	var menus []*model.SysMenu
	err := m.db.Where("status = '0'").Order("parent_id, order_num").Find(&menus).Error
	return menus, err
}

func (m *sysMenuDao) ListByParentID(parentID int64) ([]*model.SysMenu, error) {
	var menus []*model.SysMenu
	err := m.db.Where("parent_id = ? AND status = '0'", parentID).Order("order_num").Find(&menus).Error
	return menus, err
}

func (m *sysMenuDao) ListByRoleID(roleID int64) ([]*model.SysMenu, error) {
	var menus []*model.SysMenu
	err := m.db.
		Joins("LEFT JOIN sys_role_menu rm ON sys_menu.menu_id = rm.menu_id").
		Where("rm.role_id = ? AND sys_menu.status = '0'", roleID).
		Order("sys_menu.parent_id, sys_menu.order_num").
		Find(&menus).Error
	return menus, err
}

func (m *sysMenuDao) GetMenuPermsByRoleId(roleID int64) map[string]bool {
	permsSet := make(map[string]bool)

	err := m.db.Select("DISTINCT perms").
		Joins("LEFT JOIN sys_role_menu rm ON sys_menu.menu_id = rm.menu_id").
		Where("sys_menu.status = '0' AND rm.role_id = ?", roleID).
		Find(&permsSet).Error
	if err != nil {
		return nil
	}
	return permsSet
}

func (m *sysMenuDao) GetMenuTreeByUserId(userId int64) []*model.SysMenu {
	var menus []*model.SysMenu
	m.db.Table("sys_menu as m").
		Select("distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time").
		Joins("left join sys_role_menu rm on m.menu_id = rm.menu_id").
		Joins("left join sys_user_role ur on rm.role_id = ur.role_id").
		Joins("left join sys_role ro on ur.role_id = ro.role_id").
		Joins("left join sys_user u on ur.user_id = u.user_id").
		Where("u.user_id = ? and m.menu_type in ('M', 'C') and m.status = 0  AND ro.status = 0", userId).
		Order("m.parent_id, m.order_num").Find(&menus)
	return menus
}

func (m *sysMenuDao) GetMenuTreeAll() []*model.SysMenu {
	var menus []*model.SysMenu
	m.db.Table("sys_menu as m").
		Select("distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time").
		Order("m.parent_id, m.order_num").
		Find(&menus)
	return menus
}
